(function (angular) {
  'use strict';
  angular.module('FileManagerApp').service('chmod', function () {
    
    var Chmod = function (initValue) {
      this.owner = this.getRwxObj();
      this.group = this.getRwxObj();
      this.others = this.getRwxObj();
      
      if (initValue) {
        var codes = isNaN(initValue) ?
          this.convertfromCode(initValue) :
          this.convertfromOctal(initValue);
        
        if (!codes) {
          throw new Error('Invalid chmod input data (%s)'.replace('%s', initValue));
        }
        
        this.owner = codes.owner;
        this.group = codes.group;
        this.others = codes.others;
      }
    };
    
    Chmod.prototype.toOctal = function (prepend, append) {
      var result = [];
      ['owner', 'group', 'others'].forEach(function (key, i) {
        result[i] = this[key].read && this.octalValues.read || 0;
        result[i] += this[key].write && this.octalValues.write || 0;
        result[i] += this[key].exec && this.octalValues.exec || 0;
      }.bind(this));
      return (prepend || '') + result.join('') + (append || '');
    };
    
    Chmod.prototype.toCode = function (prepend, append) {
      var result = [];
      ['owner', 'group', 'others'].forEach(function (key, i) {
        result[i] = this[key].read && this.codeValues.read || '-';
        result[i] += this[key].write && this.codeValues.write || '-';
        result[i] += this[key].exec && this.codeValues.exec || '-';
      }.bind(this));
      return (prepend || '') + result.join('') + (append || '');
    };
    
    Chmod.prototype.getRwxObj = function () {
      return {
        read: false,
        write: false,
        exec: false
      };
    };
    
    Chmod.prototype.octalValues = {
      read: 4, write: 2, exec: 1
    };
    
    Chmod.prototype.codeValues = {
      read: 'r', write: 'w', exec: 'x'
    };
    
    Chmod.prototype.convertfromCode = function (str) {
      str = ('' + str).replace(/\s/g, '');
      str = str.length === 10 ? str.substr(1) : str;
      if (!/^[-rwxts]{9}$/.test(str)) {
        return;
      }
      
      var result = [], vals = str.match(/.{1,3}/g);
      for (var i in vals) {
        var rwxObj = this.getRwxObj();
        rwxObj.read = /r/.test(vals[i]);
        rwxObj.write = /w/.test(vals[i]);
        rwxObj.exec = /x|t/.test(vals[i]);
        result.push(rwxObj);
      }
      
      return {
        owner: result[0],
        group: result[1],
        others: result[2]
      };
    };
    
    Chmod.prototype.convertfromOctal = function (str) {
      str = ('' + str).replace(/\s/g, '');
      str = str.length === 4 ? str.substr(1) : str;
      if (!/^[0-7]{3}$/.test(str)) {
        return;
      }
      
      var result = [], vals = str.match(/.{1}/g);
      for (var i in vals) {
        var rwxObj = this.getRwxObj();
        rwxObj.read = /[4567]/.test(vals[i]);
        rwxObj.write = /[2367]/.test(vals[i]);
        rwxObj.exec = /[1357]/.test(vals[i]);
        result.push(rwxObj);
      }
      
      return {
        owner: result[0],
        group: result[1],
        others: result[2]
      };
    };
    
    return Chmod;
  });
})(angular);
